/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2019年6月3日
 * <修改描述:>
 */
package com.tx.core.mybatis.conditions;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import lombok.Builder;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;

/**
 * 查询条件<br/>
 *    在非MybatisPlus中，由于是借助生成sqlMap来进行实现
 *    不对having,groupby提供支持，虽然可以实现，但是需要考虑的情形太多
 *    在当前版本不支持having及groupby
 * <功能详细描述>
 *
 * @author Administrator
 * @version [版本号, 2019年6月3日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Builder
public class QuerierWrapper<T> extends AbstractWrapper<T, String, QuerierWrapper<T>>
        implements Query<QuerierWrapper<T>, T, String> {

    protected final SharedString sqlSelect = new SharedString();

    private boolean checkSqlInjection;

    /** 构造函数 */
    public QuerierWrapper() {
        this((T) null);
    }

    /** 构造函数 */
    public QuerierWrapper(T entity) {
        super.setEntity(entity);
        super.initNeed();
    }

    /** 构造函数 */
    public QuerierWrapper(Class<T> entityClass) {
        super.setEntityClass(entityClass);
        super.initNeed();
    }

    /** 构造函数 */
    public QuerierWrapper(T entity, String... columns) {
        super.setEntity(entity);
        super.initNeed();
        this.select(columns);
    }

    /**
     * 非对外公开的构造方法,只用于生产嵌套 sql
     *
     * @param entityClass 本不应该需要的
     */
    private QuerierWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
                           Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
                           SharedString paramAlias,
                           SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
        super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.paramAlias = paramAlias;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }

    /**
     * 构建查询条件实例<br/>
     *
     * @return
     */
    @Override
    protected QuerierWrapper<T> instance() {
        return new QuerierWrapper<T>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs,
                new MergeSegments(),
                paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
    }

    /**
     * 设置查询字段<br/>
     *
     * @param condition 执行条件
     * @param columns   字段列表
     * @return
     */
    @Override
    public QuerierWrapper<T> select(boolean condition, List<String> columns) {
        if (condition && CollectionUtils.isNotEmpty(columns)) {
            this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns));
        }
        return typedThis;
    }

    /**
     * 设置查询字段<br/>
     *
     * @param entityClass
     * @param predicate   过滤方式
     * @return
     */
    @Override
    public QuerierWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
        super.setEntityClass(entityClass);
        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate));
        return typedThis;
    }

    /**
     * 开启sql注入检查
     *
     * @return
     */
    public QuerierWrapper<T> checkSqlInjection() {
        this.checkSqlInjection = true;
        return this;
    }


    /**
     * 对字段进行注入检查
     *
     * @param column
     * @return
     */
    protected String columnToString(String column) {
        if (checkSqlInjection && SqlInjectionUtils.check(column)) {
            throw new MybatisPlusException("Discovering SQL injection column: " + column);
        }
        return column;
    }

    public String getSqlSelect() {
        return sqlSelect.getStringValue();
    }

    /**
     * 清空查询
     */
    @Override
    public void clear() {
        super.clear();
        sqlSelect.toNull();
    }

    /**
     * 构建器<br/>
     * @return
     * @param <T>
     */
    public static <T> QuerierWrapperBuilder<T> builder() {
        return new QuerierWrapperBuilder<T>();
    }

    /**
     * 构建器定义<br/>
     * @return
     * @param <T>
     */
    public static class QuerierWrapperBuilder<T> {

        private QuerierWrapperBuilder() {}

        public QuerierWrapper<T> build() {
            return new QuerierWrapper();
        }
    }
}
